package app.framework.front.interceptor;

import app.framework.config.FrontGlobalConfig;
import app.framework.constant.FrontConstants;
import app.framework.freemarker.util.FreeMarkerConfigUtil;
import app.framework.freemarker.util.RenderHtmlUtil;
import app.framework.freemarker.util.RenderJsUtil;
import app.framework.freemarker.util.RenderUtilConfig;
import app.framework.front.handler.ConcurrentMapFrontCache;
import app.framework.front.handler.DefaultFrontHandler;
import app.framework.front.handler.FrontCache;
import app.framework.front.handler.FrontConfig;
import app.framework.front.handler.FrontHandler;
import app.framework.front.util.WebUtil;
import freemarker.template.Configuration;
import jasmine.framework.common.util.CheckUtil;
import jasmine.framework.common.util.JsonUtil;
import jasmine.framework.common.util.NewUtil;
import jasmine.framework.context.InitSupport;
import jasmine.framework.context.RuntimeProvider;
import jasmine.framework.web.entity.WebResult;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.MessageSource;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>
 * 前端资源拦截器。
 * </p>
 *
 * @author mh.z
 */
public class FrontInterceptor implements HandlerInterceptor, InitSupport {
    private FrontHandler frontHandler;
    private Boolean devEnabled;

    private static final String REFRESH_CONTEXT_REQUEST_URL = "/dev/refreshFrontContext";
    private static final String DEV_CONFIG_REQUEST_URL = "/dev/getFrontConfig";

    private static final String NODE_JS_SUFFIX = ".node.js";
    private static final String JSX_FILE_SUFFIX = ".jsx";
    private static final String I18N_FILE_SUFFIX = ".i18n.properties";
    private static final String PAGE_CONFIG_FILE_SUFFIX = ".conf.json";

    public FrontHandler getFrontHandler() {
        return frontHandler;
    }

    @Override
    public void init(RuntimeProvider runtimeProvider) {
        FrontGlobalConfig frontGlobalConfig = runtimeProvider.getByType(FrontGlobalConfig.class);
        MessageSource messageSource = runtimeProvider.getByType(MessageSource.class);
        ServerProperties serverProperties = runtimeProvider.getByType(ServerProperties.class);
        devEnabled = Boolean.TRUE.equals(frontGlobalConfig.getShowDevTools());

        String resourceLocation = CheckUtil.notNull(frontGlobalConfig.getResourceLocation());
        Boolean resourceCached = Boolean.TRUE.equals(frontGlobalConfig.getResourceCached());
        String pageConfigLocationPattern = CheckUtil.notNull(frontGlobalConfig.getPageConfigLocationPattern());
        String i18nResourceLocationPattern = CheckUtil.notNull(frontGlobalConfig.getI18nResourceLocationPattern());

        RenderUtilConfigBuilder utilConfigBuilder = new RenderUtilConfigBuilder();
        utilConfigBuilder.setFrontGlobalConfig(frontGlobalConfig);
        utilConfigBuilder.setServerProperties(serverProperties);
        RenderUtilConfig utilConfig = utilConfigBuilder.build();
        // 初始工具类
        RenderHtmlUtil.initUtil(utilConfig);
        RenderJsUtil.initUtil(utilConfig);

        Integer resourceCacheTimeout = 0;
        FrontCache frontCache = null;

        if (Boolean.TRUE.equals(resourceCached)) {
            resourceCacheTimeout = 604800;
            frontCache = new ConcurrentMapFrontCache();
        }

        // 模板引擎配置
        Configuration freeMarkerConfig = FreeMarkerConfigUtil
                .createConfiguration(resourceLocation, resourceCacheTimeout);

        // 前端配置
        FrontConfig frontConfig = new FrontConfig();
        frontConfig.setFreeMarkerConfig(freeMarkerConfig);
        frontConfig.setMessageSource(messageSource);
        frontConfig.setPageConfigLocationPattern(pageConfigLocationPattern);
        frontConfig.setI18nResourceLocationPattern(i18nResourceLocationPattern);
        frontConfig.setScriptUrlPrefix(FrontConstants.URL_PREFIX_APP);

        // 创建前端处理器
        frontHandler = DefaultFrontHandler.create(frontConfig, frontCache);
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String originalUrl = WebUtil.getRequestUriWithoutContextPath(request);

        if (originalUrl.startsWith(FrontConstants.URL_PREFIX_STATIC)
                || originalUrl.startsWith(FrontConstants.URL_PREFIX_API)
                || originalUrl.endsWith(NODE_JS_SUFFIX)
                || originalUrl.endsWith(JSX_FILE_SUFFIX)
                || originalUrl.endsWith(I18N_FILE_SUFFIX)
                || originalUrl.endsWith(PAGE_CONFIG_FILE_SUFFIX)) {
            return true;
        }

        if (originalUrl.equals(REFRESH_CONTEXT_REQUEST_URL)) {
            // 如果未启用开发模式则不允许调用该接口
            if (!Boolean.TRUE.equals(devEnabled)) {
                return true;
            }

            // 处理刷新前端上下文的请求
            doHandleRefreshFrontContextRequest(request, response);

            return false;
        } else if (originalUrl.equals(DEV_CONFIG_REQUEST_URL)) {
            // 处理获取开发配置的请求
            doHandleGetDevConfigRequest(request, response);

            return false;
        }

        // 处理获取前端资源的请求
        boolean result = frontHandler.handleRequest(request, response);

        return !result;
    }

    /**
     * 处理刷新前端上下文的请求
     *
     * @param request
     * @param response
     * @throws Exception
     */
    protected void doHandleRefreshFrontContextRequest(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // 设置响应体信息
        WebUtil.setResponseInfo(response, HttpServletResponse.SC_OK,
                FrontConstants.RESPONSE_CONTENT_TYPE_JSON);

        // 刷新前端处理器的上下文
        frontHandler.refreshContext();
        // 返回响应信息
        String json = JsonUtil.toJson(WebResult.success());
        WebUtil.writeAndFlush(response, json);
    }

    /**
     * 处理获取开发配置的请求
     *
     * @param request
     * @param response
     * @throws Exception
     */
    protected void doHandleGetDevConfigRequest(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // 设置响应体信息
        WebUtil.setResponseInfo(response, HttpServletResponse.SC_OK,
                FrontConstants.RESPONSE_CONTENT_TYPE_JSON);

        WebResult result = WebResult.success(NewUtil.asMap("showDevTools", devEnabled));
        // 返回响应信息
        String json = JsonUtil.toJson(result);
        WebUtil.writeAndFlush(response, json);
    }

}
